﻿#include "routetbl.h"
#include "mblock.h"

static nlist_t route_list;
static mblock_t route_mblock;
static route_entry_t route_tbl[IP_RTTABLE_SIZE];

#if DBG_DISP_ENABLED(DBG_ROUTETBL_LEVEL)
    static void display_routetbl()
    {
        plat_printf("------------------route table------------------------\n");
        plat_printf("%15s %15s %15s    %s\n", "Network_Dest", "Netmask", "Gateway", "netif");
        nlist_node_t *p;
        nlist_for_each(p, &route_list)
        {
            route_entry_t *entry = (route_entry_t *)nlist_entry(route_entry_t, node, p);
            dbg_dump_ipaddr_oneline("", &entry->net);
            dbg_dump_ipaddr_oneline("  ", &entry->mask);
            dbg_dump_ipaddr_oneline("  ", &entry->next_hop);
            plat_printf("  %s\n", entry->netif->name);
        }
        plat_printf("-----------------------------------------------------\n");
    }
#else
    #define display_routetbl()
#endif

/**
 * @brief 路由表相关初始化
 */
net_err_t routetbl_init()
{
    nlist_init(&route_list);
    mblock_init(&route_mblock, route_tbl, sizeof(route_entry_t), IP_RTTABLE_SIZE, NLOCKER_NONE);

    return NET_ERR_OK;
}

/**
 * @brief 向路由表中添加表项
 * @param net 网络目标
 * @param mask 网络掩码
 * @param next_hop 网关
 * @param netif 网络接口
 */
net_err_t routetbl_add(ipaddr_t *net, ipaddr_t *mask, ipaddr_t *next_hop, netif_t *netif)
{
    route_entry_t *entry = mblock_alloc(&route_mblock, -1);
    if (!entry)
    {
        DBG_WARNING(DBG_ROUTETBL_LEVEL, "add route table entry failed!");
        return NET_ERR_FULL;
    }

    plat_memset(entry, 0, sizeof(route_entry_t));
    if (net)
    {
        ipaddr_copy(&entry->net, net);
    }
    if (mask)
    {
        ipaddr_copy(&entry->mask, mask);
    }
    if (next_hop)
    {
        ipaddr_copy(&entry->next_hop, next_hop);
    }
    entry->netif = netif;
    entry->mask_one_cnt = ipaddr_one_cnt(&entry->mask);

    nlist_insert_last(&route_list, &entry->node);

    display_routetbl();
    return NET_ERR_OK;
}

/**
 * @brief 删除路由表中对应的表项
 * @param net 网络地址
 * @param mask 网络掩码
 */
net_err_t routetbl_remove(ipaddr_t *net, ipaddr_t *mask)
{
    nlist_node_t *p;
    nlist_for_each(p, &route_list)
    {
        route_entry_t *entry = (route_entry_t *)nlist_entry(route_entry_t, node, p);
        if(ipaddr_is_equal(&entry->net, net) && ipaddr_is_equal(&entry->mask, mask)){
            nlist_remove(&route_list, p);
            mblock_free(&route_mblock, entry);
        }
    }

    display_routetbl();
    return NET_ERR_OK;
}

/**
 * @brief 根据目的ip查找合适的路由表项
 */
route_entry_t * routetbl_find(const ipaddr_t *ip)
{
    route_entry_t *ret = NULL;
    nlist_node_t *p;
    nlist_for_each(p, &route_list)
    {
        route_entry_t *entry = (route_entry_t *)nlist_entry(route_entry_t, node, p);
        ipaddr_t ip_net = ipaddr_get_net(ip, &entry->mask);
        if(!ipaddr_is_equal(&ip_net, &entry->net)) continue;

        if(!ret)
        {
            ret = entry;
            continue;
        }
        ret = entry->mask_one_cnt > ret->mask_one_cnt ? entry : ret;
    }

    return ret;
}